﻿
namespace Aeon.Emulator.Instructions.BitShifting
{
    internal static class Shl
    {
        [Opcode("D0/4 rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteShiftLeft1(Processor p, ref byte dest)
        {
            p.CarryFlag = (dest & 0x80) == 0x80;
            p.OverflowFlag = (dest & 0xC0) == 0x80 || (dest & 0xC0) == 0x40;
            dest <<= 1;
            p.ByteUpdateFlags((sbyte)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("D2/4 rmb,cl|C0/4 rmb,ib", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteShiftLeft(Processor p, ref byte dest, byte count)
        {
            count &= 0x1F;
            if(count == 0)
                return;
            else if(count == 1)
            {
                ByteShiftLeft1(p, ref dest);
                return;
            }

            dest <<= count - 1;
            p.CarryFlag = (dest & 0x80) != 0;
            dest <<= 1;
            p.ByteUpdateFlags((sbyte)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("D1/4 rmw", AddressSize = 16 | 32)]
        public static void WordShiftLeft1(Processor p, ref ushort dest)
        {
            if((dest & 0x8000) == 0x8000)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            if((dest & 0xC000) == 0x8000 || (dest & 0xC000) == 0x4000)
                p.Flags |= EFlags.Overflow;
            else
                p.Flags &= ~EFlags.Overflow;

            dest <<= 1;
            p.WordUpdateFlags((short)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordShiftLeft1", AddressSize = 16 | 32)]
        public static void DWordShiftLeft1(Processor p, ref uint dest)
        {
            if((dest & 0x80000000) == 0x80000000)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            if((dest & 0xC0000000) == 0x80000000 || (dest & 0xC0000000) == 0x40000000)
                p.Flags |= EFlags.Overflow;
            else
                p.Flags &= ~EFlags.Overflow;

            dest <<= 1;
            p.DWordUpdateFlags((int)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("D3/4 rmw,cl|C1/4 rmw,ib", AddressSize = 16 | 32)]
        public static void WordShiftLeft(Processor p, ref ushort dest, byte count)
        {
            count &= 0x1F;
            if(count == 0)
                return;
            else if(count == 1)
            {
                WordShiftLeft1(p, ref dest);
                return;
            }

            dest <<= count - 1;
            if((dest & 0x8000) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            dest <<= 1;
            p.WordUpdateFlags((short)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordShiftLeft", AddressSize = 16 | 32)]
        public static void DWordShiftLeft(Processor p, ref uint dest, byte count)
        {
            count &= 0x1F;
            if(count == 0)
                return;
            else if(count == 1)
            {
                DWordShiftLeft1(p, ref dest);
                return;
            }

            dest <<= count - 1;
            if((dest & 0x80000000) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            dest <<= 1;
            p.DWordUpdateFlags((int)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
    }

    internal static class Shr
    {
        [Opcode("D0/5 rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteShiftRight1(Processor p, ref byte dest)
        {
            if((dest & 0x01) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            if((dest & 0x80) == 0x80)
                p.Flags |= EFlags.Overflow;
            else
                p.Flags &= ~EFlags.Overflow;

            dest >>= 1;
            p.ByteUpdateFlags((sbyte)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        
        [Opcode("D2/5 rmb,cl|C0/5 rmb,ib", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteShiftRight(Processor p, ref byte dest, byte count)
        {
            count &= 0x1F;
            if(count == 0)
                return;
            else if(count == 1)
            {
                ByteShiftRight1(p, ref dest);
                return;
            }

            dest >>= count - 1;

            if((dest & 0x01) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;
            
            dest >>= 1;
            p.ByteUpdateFlags((sbyte)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("D1/5 rmw", AddressSize = 16 | 32)]
        public static void WordShiftRight1(Processor p, ref ushort dest)
        {
            if((dest & 0x0001) == 0x0001)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            if((dest & 0x8000) == 0x8000)
                p.Flags |= EFlags.Overflow;
            else
                p.Flags &= ~EFlags.Overflow;

            dest >>= 1;
            p.WordUpdateFlags((short)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordShiftRight1", AddressSize = 16 | 32)]
        public static void DWordShiftRight1(Processor p, ref uint dest)
        {
            if((dest & 0x00000001) == 0x00000001)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            if((dest & 0x80000000) == 0x80000000)
                p.Flags |= EFlags.Overflow;
            else
                p.Flags &= ~EFlags.Overflow;

            dest >>= 1;
            p.DWordUpdateFlags((int)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("D3/5 rmw,cl|C1/5 rmw,ib", AddressSize = 16 | 32)]
        public static void WordShiftRight(Processor p, ref ushort dest, byte count)
        {
            count &= 0x1F;
            if(count == 0)
                return;
            else if(count == 1)
            {
                WordShiftRight1(p, ref dest);
                return;
            }

            dest >>= count - 1;

            if((dest & 0x0001) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;
            
            dest >>= 1;
            p.WordUpdateFlags((short)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordShiftRight", AddressSize = 16 | 32)]
        public static void DWordShiftRight(Processor p, ref uint dest, byte count)
        {
            count &= 0x1F;
            if(count == 0)
                return;
            else if(count == 1)
            {
                DWordShiftRight1(p, ref dest);
                return;
            }

            dest >>= count - 1;

            if((dest & 0x00000001) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            dest >>= 1;
            p.DWordUpdateFlags((int)dest, dest, EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
    }
}
